workerpool 是一個可以同時用在 node.js 跟 瀏覽器(web worker) 的套件,實作了 Thread pool 的概念,讓程式可以做到高效的平行運算
Thread pool
可以視為一連串的運算單元合併起來的架構,下圖綠色部分共有六個 thread 可以分別執行不同的運算,每一個 thread 在瀏覽器中對應到的就是一個 web worker 線程
在一開始會有許多待辦的 tasks 放入 task queue 中,等待 thread pool
中有其中一個 thread 完成任務後,就會從 task queue 中拿取新的 task 到 thread pool
中執行,而 thread pool 中執行完的任務就會放到 completed tasks
圖片來源:wiki 執行緒池
接著讓我們來看看 workerpool
的一些基本用法
我們可以在主線程中,引入 workerpool
的 cdn 連結,接著使用 workerpool.pool
,創建出 thread pool
,其中的 maxWorkers
代表這個 thread pool
最多可以開啟幾個線程,如果沒有傳 maxWorkers
的話預設的上限會是 CPU 的核心數 - 1 的 worker 線程
import workerpool from 'https://cdn.jsdelivr.net/npm/workerpool@6.5.0/+esm';
// 創建最多 8 個 worker 線程的 thread pool
const pool = workerpool.pool({ maxWorkers: 8 });
創建出 pool
之後可以使用 pool.exec()
呼叫要執行的函式,每次執行 pool.exec()
時,背後就會自動創建出一個 worker 線程來執行任務的運算,以下在 worker
線程中執行加法,算完後才把結果 7 丟回主線程
function add(a, b) {
return a + b;
}
pool
.exec(add, [3, 4])
.then(function (result) {
console.log('result', result); // outputs 7
})
瞭解了 workerpool
的基礎用法後,一樣讓我們藉由範例來看如何使用 workerpool
做到高效的平行運算吧
使用多個 web worker 組成的 workerpool
進行平行運算,確認執行速度是否變快
numberCount
決定在多少數字以下尋找質數,例如:numberCount: 10
,找到的質數應該是 [2, 3, 5, 7]
workerCount
使用 worker 的數量,這代表在 workerpool
中最多會使用到幾個 worker 線程做運算
使用電腦 CPU 核心數 (hardwareConcurrecy)
hardwareConcurrecy 可以取得電腦中邏輯運算單元的數量,這也代表著理論上最多可同時運行的 worker 數量,例如:從我的電腦中可以查到 CPU 核心的數量是 10,而瀏覽器實作 hardwareConcurrecy
時通常會回傳較低數量的邏輯運算單元,所以在我的 chrome 瀏覽器中回傳的 hardwareConcurrecy
等於 8
當勾選 使用電腦 CPU 核心數 (hardwareConcurrecy) 後,使用 worker 的數量(workerCount) 就會被固定成 hardwareConcurrecy
回傳的值
按下 Run 按鈕後會根據 尋找質數的上限數字(numberCount)
、使用 worker 數(workerCount)
在 worker 線程中找出所有的質數
執行完後,運算的時間
會以表格方式顯示出來
根據不同的 使用 worker 數(workerCount)
,查看執行效率的差異
一開始會根據填入的 使用 worker 數(workerCount),決定 workerpool
中最多可運行的 worker 線程數量
const pool = workerpool.pool({ maxWorkers: workerCount });
這裡會將 尋找質數的上限數字(numberCount)、使用 worker 數(workerCount) 丟入 partition
函數中,分割出要尋找質數的數字群們,例如:numberCount: 100 萬
workerCount: 10
會把 100 萬個數字分割成 10 個群組,第一個群是 1~10 萬,接著 10~20萬,直到最後一群組是 90~100萬
const partitionArray = partition({ numberCount, workerCount });
接著使用 pool.exec()
於各 worker 線程中執行 尋找質數(findPrimes)
函式
const promises = partitionArray.map((partitionList) => {
// 將分割出來的數字丟入 findPrimes 中
return pool.exec(findPrimes, [partitionList]);
});
const results = await Promise.all(promises);
// 所有找到的質數 (ex. [2, 3, 5, ...])
const primes = results.flat();
雖然這個範例沒有做到嚴格的數據統計,而且不同資料量、不同的運算時間,適合創建的 worker 線程數量可能也都不一定,但基本上以 電腦 CPU 核心數 (hardwareConcurrecy) 創建出 thread pool
線程的數量上限,應該可以充分運用 CPU 資源,縮短整體運算的時間
workerpool github repo
wiki 執行緒池
Number of Web Workers Limit